package classesdemo;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

public class EnvironmentManager {
    // each Thread will have their own Thread local instance of the EM
    private static final ThreadLocal<EnvironmentManager> threadLocalManager =
            new ThreadLocal<EnvironmentManager>() {
                @Override
                protected EnvironmentManager initialValue() {
                    return new EnvironmentManager();
                }
            };

    public static EnvironmentManager getEnvironmentManagerInstance() {
        return threadLocalManager.get();
    }

    private final Map<File, Long> fileLastModifiedMap = new HashMap<File, Long>();
    private Properties allProperties = null;
//    private Logger logger;

    private EnvironmentManager() {
//        logger = new Logger();
        final ThreadFactory defaultThreadFactory = Executors.defaultThreadFactory();
        final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = defaultThreadFactory.newThread(r);
                thread.setDaemon(true);
                return thread;
            }
        });
        scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                boolean changed = false;
                Set<File> files = fileLastModifiedMap.keySet();
                for (File file : files) {
                    long oldLastModified = fileLastModifiedMap.get(file);
                    if (!file.exists() || file.lastModified() != oldLastModified) {
                        changed = true;
                        break;
                    }
                }

                //System.out.println("Changed:" + changed);
                if (changed) {
                    try {
                        initProperties();
                    } catch (Exception e) {
//                        logger.error("init properties error.", e);
                    }
                }
            }
        }, 0, 60L, TimeUnit.SECONDS);
    }

    public static Properties getAppProperties() throws Exception {
        return threadLocalManager.get().getAllAppProperties();
    }

    public synchronized Properties getAllAppProperties() throws Exception {
        if (allProperties != null) {
            return allProperties;
        }

        initProperties();
        return allProperties;
    }

    public void initProperties() throws Exception {
        String propertyFiles;
        if (System.getProperties().containsKey("application_properties")) {
            propertyFiles = System.getProperty("application_properties");
        } else if (System.getenv().containsKey("application_properties")) {
            propertyFiles = System.getenv("application_properties");
        } else {
            throw new Exception("should set application_properties in environment variable.");
        }
        String[] filePaths = propertyFiles.split(",");
        initProperties(filePaths);
    }

    public synchronized void initProperties(String[] filePaths) throws Exception {
        reset();
        for (String file : filePaths) {
            try {
                if (file.startsWith("file:")) {
                    parseFile(file);
                } else if (file.startsWith("classpath:")) {
                    parseClassPathFile(file);
                } else {
                    throw new IllegalArgumentException("Can not identify the file type:" + file);
                }
            } catch (IOException e) {
                throw new Exception(e);
            }
        }
    }

    public void parseFile(String f) throws IOException {
        String filePath = f.substring(5, f.length());
        File file = new File(filePath);
        FileInputStream fis = new FileInputStream(file);
        if (allProperties == null) {
            allProperties = new Properties();
        }
        allProperties.load(fis);
        fileLastModifiedMap.put(file, file.lastModified());

    }

    public void parseClassPathFile(String f) throws IOException {
        String filePath = f.substring(10, f.length());
        InputStream is = Thread.currentThread().getContextClassLoader()
                .getResourceAsStream(filePath);
        if (allProperties == null) {
            allProperties = new Properties();
        }
        allProperties.load(is);
    }

    public void setFiles(String[] filePaths) throws Exception {
        initProperties(filePaths);
    }

    private void reset() {
        this.fileLastModifiedMap.clear();
        this.allProperties = null;
    }

    public static void main(String[] args) throws Exception {
        System.setProperty("application_properties", "file:d:\\etl.properties,file:d:\\etl_bootstrap.properties");
        //did check application.property later, so no use
//        System.getProperties().put("application.property", "file:d:\\sample2.properties,file:d:\\sample1.properties");

        EnvironmentManager em = EnvironmentManager.getEnvironmentManagerInstance();
        Properties props = em.getAllAppProperties();

        //loop through properties
        for(Map.Entry<Object, Object> e : props.entrySet()) {
            System.out.println(e);
        }
        
        for (Map.Entry<File, Long> en: em.fileLastModifiedMap.entrySet()){
           System.out.println(en);
        }
        
        while (true) {
            Thread.sleep(3 * 1000L);
            System.out.println(em.getAllAppProperties().get("test1"));
        }
    }
}
